/***************************************************************************
 *   Copyright (C) 2012~2012 by Tai-Lin Chu                                *
 *   tailinchu@gmail.com                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
 ***************************************************************************/
#include "eim.h"
#include "config.h"
#include "fcitx/ime-internal.h"
#include "oyun_im.h"
#include "server.h"
#include <codecvt>
#include <cstdlib>
#include <ctype.h>
#include <errno.h>
#include <fcitx-config/fcitx-config.h>
#include <fcitx-config/hotkey.h>
#include <fcitx-config/xdg.h>
#include <fcitx-utils/log.h>
#include <fcitx-utils/utf8.h>
#include <fcitx-utils/utils.h>
#include <fcitx/context.h>
#include <fcitx/ime.h>
#include <fcitx/instance.h>
#include <fcitx/keys.h>
#include <fcitx/ui.h>
#include <iostream>
#include <libintl.h>
#include <locale>
#include <regex>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/mman.h>
#include <sys/wait.h>

using namespace std;

// 最大字长
#define MAX_WORD_LEN 50
// 候选词数量
#define CAND_WORD_NUM 10

FCITX_DEFINE_PLUGIN(fcitx_en, ime2, FcitxIMClass2) = {
    FcitxEnCreate, FcitxEnDestroy, NULL, NULL, NULL, NULL, NULL, NULL,
};

CONFIG_DESC_DEFINE(GetFcitxEnConfigDesc, "fcitx-en.desc")

static INPUT_RETURN_VALUE FcitxEnGetCandWord(void *arg,
                                             FcitxCandidateWord *candWord);

static void FcitxEnReloadConfig(void *arg);

void UpdateCodeInputEn(FcitxEn *en);

static boolean LoadEnConfig(FcitxEnConfig *fs);

static void SaveEnConfig(FcitxEnConfig *fs);

static void ConfigEn(FcitxEn *en);

void start_tray_process();
int tray_num = 0;
void start_tray_process() {
    /* exec command */
    pid_t child_pid;

    child_pid = fork();
    if (child_pid < 0) {
        perror("fork");
    } else if (child_pid == 0) { /* child process  */
        setsid();
        pid_t grandchild_pid;

        grandchild_pid = fork();
        if (grandchild_pid < 0) {
            perror("fork");
            _exit(1);
        } else if (grandchild_pid == 0) { /* grandchild process  */
            // 构造命令行字符串
            // 运行命令行
            system("/usr/share/fcitx/Oyun/OyunUI/oyun/bin/python "
                   "/usr/share/fcitx/Oyun/OyunUI/trayWindow.py");
            perror("open_tray");
            _exit(1);
        } else {
            _exit(0);
        }
    } else { /* parent process */
        int status;
        waitpid(child_pid, &status, 0);
    }
}

const FcitxHotkey COMMAND_CHARACTER1[2] = {
    {NULL, FcitxKey_minus, FcitxKeyState_None},       // -
    {NULL, FcitxKey_underscore, FcitxKeyState_None}}; // _
const FcitxHotkey COMMAND_CHARACTER2[2] = {
    {NULL, FcitxKey_grave, FcitxKeyState_None},     // `
    {NULL, FcitxKey_quotedbl, FcitxKeyState_None}}; // "
const FcitxHotkey COMMAND_CHARACTER3[2] = {
    {NULL, FcitxKey_apostrophe, FcitxKeyState_None},   // '
    {NULL, FcitxKey_asciicircum, FcitxKeyState_None}}; // ^
const FcitxHotkey COMMAND_CHARACTER4[2] = {
    {NULL, FcitxKey_asterisk, FcitxKeyState_None},   // *
    {NULL, FcitxKey_ampersand, FcitxKeyState_None}}; // &

const FcitxHotkey MENGKELI_CHARACTER[2] = {
    {NULL, FcitxKey_semicolon, FcitxKeyState_None}, // ;
    {NULL, FcitxKey_None, FcitxKeyState_None}};

const FcitxHotkey FCITX_MINUS[2] = {
    {NULL, FcitxKey_bracketleft, FcitxKeyState_None}, // [
    {NULL, FcitxKey_Page_Up, FcitxKeyState_None}};

const FcitxHotkey FCITX_PLUS[2] = {
    {NULL, FcitxKey_bracketright, FcitxKeyState_None}, // ]
    {NULL, FcitxKey_Page_Down, FcitxKeyState_None}};

const FcitxHotkey SYMBOL_CHARACTER[2] = {
    {NULL, FcitxKey_question, FcitxKeyState_None}, // ?
    {NULL, FcitxKey_None, FcitxKeyState_None}};

FCITX_EXPORT_API
boolean FcitxHotkeyIsHotKeyUpperInOyun(FcitxKeySym sym, unsigned int state) {
    if (state)
        return false;

    if (sym == FcitxKey_C || sym == FcitxKey_E || sym == FcitxKey_H ||
        sym == FcitxKey_K || sym == FcitxKey_L || sym == FcitxKey_N ||
        sym == FcitxKey_Q || sym == FcitxKey_R || sym == FcitxKey_Z)
        return true;

    return false;
}

IM_Config *create_shared_memory() {
    return (IM_Config *)mmap(NULL, sizeof(IM_Config), PROT_READ | PROT_WRITE,
                             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
}

/**
 * @brief initialize the extra input method
 *
 * @param arg
 * @return successful or not
 **/
__EXPORT_API void *FcitxEnCreate(FcitxInstance *instance) {
    if (GetFcitxEnConfigDesc() == NULL)
        return NULL;
    FcitxEn *en = (FcitxEn *)fcitx_utils_malloc0(sizeof(FcitxEn));
    //    FcitxGlobalConfig *config = FcitxInstanceGetGlobalConfig(instance);
    FcitxInputState *input = FcitxInstanceGetInputState(instance);
    FcitxCandidateWordSetChoose(FcitxInputStateGetCandidateList(input),
                                DIGIT_STR_CHOOSE);

    bindtextdomain("fcitx-en", LOCALEDIR);
    en->engine = new OyunIM(create_shared_memory());
    en->server = new httplib::Server();
    en->owner = instance;
    en->cur = 0;
    en->buf = strdup("");
    en->num_flag = 0;
    en->minute_of_last_reset = std::chrono::system_clock::now();
    en->day_of_last_reset = std::chrono::system_clock::now();
    start_process(en);

    FcitxCandidateWordSetPageSize(FcitxInputStateGetCandidateList(input), 10);
    LoadEnConfig(&en->config);
    ConfigEn(en);
    FcitxIMIFace iface;
    memset(&iface, 0, sizeof(FcitxIMIFace));
    iface.Init = FcitxEnInit;
    iface.ResetIM = FcitxEnReset;
    iface.DoInput = FcitxEnDoInput;
    iface.GetCandWords = FcitxEnGetCandWords;
    iface.Save = NULL;
    iface.KeyBlocker = NULL;
    iface.ReloadConfig = FcitxEnReloadConfig;
    iface.OnClose = NULL;
    iface.KeyBlocker = NULL;
    FcitxInstanceRegisterIMv2(instance, en, "Oyun", _("Oyun"), "fcitx-en",
                              iface, 1, "mn_CN");
    return en;
}

/**
 * @brief Process Key Input and return the status
 *
 * @param keycode keycode from XKeyEvent
 * @param state state from XKeyEvent
 * @param count count from XKeyEvent
 * @return INPUT_RETURN_VALUE
 **/
__EXPORT_API INPUT_RETURN_VALUE FcitxEnDoInput(void *arg, FcitxKeySym sym,
                                               unsigned int state) {
    FcitxEn *en = (FcitxEn *)arg;
    FcitxInputState *input = FcitxInstanceGetInputState(en->owner);
    unsigned int buf_len = strlen(en->buf);
    INPUT_RETURN_VALUE retVal;
    FcitxCandidateWordList *candList = FcitxInputStateGetCandidateList(input);
    // 符号输入
    if (FcitxHotkeyIsHotKey(sym, state, SYMBOL_CHARACTER) &&
        strlen(en->engine->imConfig->asr_content) != 0) {
        strcpy(FcitxInputStateGetOutputString(input),
               en->engine->imConfig->asr_content);
        en->engine->imConfig->asr_content[0] = '\0';
        return IRV_COMMIT_STRING;
    }
    if (sym == 0 && state == 0)
        sym = FcitxKey_VoidSymbol;

    retVal = IRV_TO_PROCESS;
//    if (!FcitxHotkeyIsHotKeyUpperInOyun(sym, state)) {
//        if (strlen(en->buf) == 0) {
//            char in = (char)sym & 0xff;
//            char *half1 = strndup(en->buf, en->cur);
//            char *half2 = strdup(en->buf + en->cur);
//            en->buf = static_cast<char *>(realloc(en->buf, buf_len + 2));
//            sprintf(en->buf, "%s%c%s", half1, in, half2);
//            en->cur++;
//            free(half1);
//            free(half2);
//            strcpy(FcitxInputStateGetOutputString(input), en->buf);
//            return IRV_COMMIT_STRING;
//        } else {
//            return IRV_DO_NOTHING;
//        }
//    }
    /* is not in special state */
    if (FcitxHotkeyIsHotKeyLAZ(sym, state) ||
        FcitxHotkeyIsHotKeyUAZ(sym, state) ||
        FcitxHotkeyIsHotKey(sym, state, COMMAND_CHARACTER1) ||
        FcitxHotkeyIsHotKey(sym, state, COMMAND_CHARACTER2) ||
        FcitxHotkeyIsHotKey(sym, state, COMMAND_CHARACTER3) ||
        FcitxHotkeyIsHotKey(sym, state, COMMAND_CHARACTER4) ||
        (!en->engine->imConfig->get_key_switch() &&
         FcitxHotkeyIsHotKey(sym, state, MENGKELI_CHARACTER))) {

        // 每分钟更新一次kpm
        auto now = std::chrono::system_clock::now();
        auto duration1 = std::chrono::duration_cast<std::chrono::minutes>(
            now - en->minute_of_last_reset);
        if (duration1.count() >= 1) {
            en->engine->imConfig->set_kpm(0);
            en->minute_of_last_reset = now;
        }
        en->engine->imConfig->set_kpm(en->engine->imConfig->get_kpm() + 1);

        if (FcitxInputStateGetIsInRemind(input)) {
            en->cur = 0;
            en->buf[0] = '\0';
            FcitxInstanceCleanInputWindow(en->owner);
            FcitxUIUpdateInputWindow(en->owner);
        }
        if (en->cur == MAX_WORD_LEN)
            return IRV_DO_NOTHING;
        FcitxInputStateSetIsInRemind(input, false);
        FcitxInputStateSetShowCursor(input, true);

        char in = (char)sym & 0xff;
        char *half1 = strndup(en->buf, en->cur);
        char *half2 = strdup(en->buf + en->cur);
        en->buf = static_cast<char *>(realloc(en->buf, buf_len + 2));
        sprintf(en->buf, "%s%c%s", half1, in, half2);
        en->cur++;
        free(half1);
        free(half2);
        retVal = IRV_DISPLAY_CANDWORDS;
    } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_BACKSPACE)) {
        if (en->cur) {
            char *half1 = strndup(en->buf, en->cur - 1);
            char *half2 = strdup(en->buf + en->cur);
            en->buf = static_cast<char *>(realloc(en->buf, buf_len + 1));
            sprintf(en->buf, "%s%s", half1, half2);
            en->cur--;
            free(half1);
            free(half2);
            if (!strlen(en->buf)) {
                retVal = IRV_CLEAN;
            } else {
                retVal = IRV_DISPLAY_CANDWORDS;
            }
        } else {
            if (!strlen(en->buf)) {
                retVal = IRV_TO_PROCESS;
            } else {
                retVal = IRV_DO_NOTHING;
            }
        }
    } else if (FcitxInputStateGetRawInputBufferSize(input) &&
               FcitxHotkeyIsHotKey(sym, state, FCITX_DELETE)) {
        if (en->cur == strlen(en->buf))
            retVal = IRV_DO_NOTHING;
        char *half1 = strndup(en->buf, en->cur);
        char *half2 = strdup(en->buf + en->cur + 1);
        en->buf = static_cast<char *>(realloc(en->buf, buf_len + 1));
        sprintf(en->buf, "%s%s", half1, half2);
        free(half1);
        free(half2);
        if (!strlen(en->buf)) {
            retVal = IRV_CLEAN;
        } else {
            retVal = IRV_DISPLAY_CANDWORDS;
        }
    } else if (FcitxInputStateGetRawInputBufferSize(input) &&
               FcitxHotkeyIsHotKey(sym, state, FCITX_HOME)) {
        en->cur = 0;
        retVal = IRV_DISPLAY_CANDWORDS;
    } else if (FcitxInputStateGetRawInputBufferSize(input) &&
               FcitxHotkeyIsHotKey(sym, state, FCITX_END)) {
        en->cur = strlen(en->buf);
        retVal = IRV_DISPLAY_CANDWORDS;
    } else if (FcitxInputStateGetRawInputBufferSize(input) &&
               FcitxHotkeyIsHotKey(sym, state, FCITX_RIGHT)) {
        if (en->cur == strlen(en->buf)) {
            retVal = IRV_DO_NOTHING;
        } else {
            en->cur++;
            retVal = IRV_DISPLAY_CANDWORDS;
        }
    } else if (FcitxInputStateGetRawInputBufferSize(input) &&
               FcitxHotkeyIsHotKey(sym, state, FCITX_LEFT)) {
        if (en->cur > 0) {
            en->cur--;
            retVal = IRV_DISPLAY_CANDWORDS;
        }
    } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_SPACE)) {
        if (FcitxCandidateWordPageCount(candList) == 0) {
            if (FcitxInputStateGetRawInputBufferSize(input) == 0)
                retVal = IRV_TO_PROCESS;
            else
                retVal = IRV_DO_NOTHING;
        } else {
            // 每隔一天清零一次每天输入
            auto now = std::chrono::system_clock::now();
            auto time_now = std::chrono::system_clock::to_time_t(now);
            auto time_info = std::localtime(&time_now);
            if (time_info->tm_hour == 0 && time_info->tm_min == 0) {
                en->engine->imConfig->set_today_input(0);
                en->day_of_last_reset = now;
            }
            en->engine->imConfig->set_today_input(
                en->engine->imConfig->get_today_input() + 1);
            en->engine->imConfig->set_total_input(
                en->engine->imConfig->get_total_input() + 1);

            FcitxCandidateWord *candWord;
            candWord = FcitxCandidateWordGetByIndex(candList, 0);
            if (candWord != NULL) {
                FcitxInputStateSetIsInRemind(input, true);
                size_t wordLen = strlen(candWord->strWord);
                en->strRemindSource = new char[wordLen + 1];
                std::memcpy(en->strRemindSource, candWord->strExtra,
                            wordLen + 1);
            }
            retVal = FcitxCandidateWordChooseByIndex(candList, 0);
        }
    } else if (FcitxInputStateGetRawInputBufferSize(input) &&
               FcitxHotkeyIsHotKey(sym, state, FCITX_ENTER)) {
        if (!FcitxInputStateGetIsInRemind(input)) {
            strcpy(FcitxInputStateGetOutputString(input), en->buf);
        } else {
            string tmp;
            strcpy(FcitxInputStateGetOutputString(input), tmp.c_str());
        }
        retVal = IRV_COMMIT_STRING;
    }
    if (strlen(en->buf) > 0 && FcitxCandidateWordGetListSize(candList) > 0) {
        if (FcitxHotkeyIsHotKey(sym, state, FCITX_MINUS)) {
            FcitxCandidateWordGoPrevPage(candList);
            FcitxInstanceProcessInputReturnValue(en->owner,
                                                 IRV_DISPLAY_CANDWORDS);
            return IRV_DO_NOTHING;
        } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_PLUS)) {
            FcitxCandidateWordGoNextPage(candList);
            FcitxInstanceProcessInputReturnValue(en->owner,
                                                 IRV_DISPLAY_CANDWORDS);
            return IRV_DO_NOTHING;
        }
    }

    int iKey;
    iKey = FcitxCandidateWordCheckChooseKey(candList, sym, state);
    if (retVal == IRV_TO_PROCESS) {
        if (iKey >= 0) {
            if (en->cur == 0) {
                char in = (char)sym & 0xff;
                char *half1 = strndup(en->buf, en->cur);
                char *half2 = strdup(en->buf + en->cur);
                en->buf = static_cast<char *>(realloc(en->buf, buf_len + 2));
                sprintf(en->buf, "%s%c%s", half1, in, half2);
                en->cur++;
                free(half1);
                free(half2);
                if (sym >= 0x0030 && sym <= 0x0039) {
                    en->num_flag = 1;
                    return IRV_DISPLAY_CANDWORDS;
                } else
                    return IRV_TO_PROCESS;
            }

            // 每隔一天清零一次每天输入
            auto now = std::chrono::system_clock::now();
            auto time_now = std::chrono::system_clock::to_time_t(now);
            auto time_info = std::localtime(&time_now);
            if (time_info->tm_hour == 0 && time_info->tm_min == 0) {
                en->engine->imConfig->set_today_input(0);
                en->day_of_last_reset = now;
            }
            en->engine->imConfig->set_today_input(
                en->engine->imConfig->get_today_input() + 1);
            en->engine->imConfig->set_total_input(
                en->engine->imConfig->get_total_input() + 1);

            FcitxCandidateWord *candWord;
            candWord = FcitxCandidateWordGetByIndex(candList, iKey);
            if (!FcitxInputStateGetIsInRemind(input)) {
                if (!FcitxInputStateGetRawInputBufferSize(input))
                    return IRV_TO_PROCESS;
                else if (candWord == NULL)
                    return IRV_DO_NOTHING;
            }
            if (candWord != NULL) {
                FcitxInputStateSetIsInRemind(input, true);
                size_t wordLen = strlen(candWord->strWord);
                en->strRemindSource = new char[wordLen + 1];
                std::memcpy(en->strRemindSource, candWord->strExtra,
                            wordLen + 1);
            }
        } else if (sym == FcitxKey_VoidSymbol) {
            en->cur = 0;
            retVal = IRV_DISPLAY_CANDWORDS;
        } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_ESCAPE)) {
            return IRV_TO_PROCESS;
        } else if (FcitxHotkeyIsHotKey(sym, state, FCITX_LSHIFT) &&
                   FcitxInputStateGetIsInRemind(input)) {
            string tmp;
            strcpy(FcitxInputStateGetOutputString(input), tmp.c_str());
            return IRV_COMMIT_STRING;
        }
    }

    if (!FcitxInputStateGetIsInRemind(input)) {
        UpdateCodeInputEn(en);
        FcitxInputStateSetCursorPos(input, en->cur);
        FcitxInputStateSetClientCursorPos(input, 0);
    } else {
        if (retVal == IRV_TO_PROCESS && iKey < 0) {
            en->cur = 0;
            en->buf[0] = '\0';
            FcitxInstanceCleanInputWindow(en->owner);
            FcitxUIUpdateInputWindow(en->owner);
        }
    }

    return retVal;
}

void UpdateCodeInputEn(FcitxEn *en) {
    FcitxInputState *input = FcitxInstanceGetInputState(en->owner);
    char *strCodeInput = FcitxInputStateGetRawInputBuffer(input);
    strCodeInput[0] = '\0';
    strcpy(strCodeInput, en->buf);
    FcitxInputStateSetRawInputBufferSize(input, strlen(strCodeInput));
}

__EXPORT_API boolean FcitxEnInit(void *arg) {
    FcitxEn *en = (FcitxEn *)arg;
    FcitxIM *im = FcitxInstanceGetCurrentIM(en->owner);

    if (im) {
        if (tray_num >= 3) {
            start_tray_process();
        }
        tray_num++;
    }
    FcitxInstanceSetContext(en->owner, CONTEXT_IM_KEYBOARD_LAYOUT, "us");
    boolean flag = true;
    FcitxInstanceSetContext(en->owner, CONTEXT_DISABLE_QUICKPHRASE, &flag);
    FcitxInstanceSetContext(en->owner, CONTEXT_ALTERNATIVE_PREVPAGE_KEY,
                            FCITX_MINUS);
    FcitxInstanceSetContext(en->owner, CONTEXT_ALTERNATIVE_NEXTPAGE_KEY,
                            FCITX_PLUS);
    return true;
}

__EXPORT_API void FcitxEnReset(void *arg) {
    FcitxEn *en = (FcitxEn *)arg;
    en->cur = 0;
    free(en->buf);
    en->buf = strdup("");
}

/**
 * @brief function DoInput has done everything for us.
 *
 * @param searchMode
 * @return INPUT_RETURN_VALUE
 **/
__EXPORT_API INPUT_RETURN_VALUE FcitxEnGetCandWords(void *arg) {
    FcitxEn *en = (FcitxEn *)arg;
    FcitxInputState *input = FcitxInstanceGetInputState(en->owner);
    FcitxMessages *msgPreedit = FcitxInputStateGetPreedit(input);
    FcitxMessages *msgClientPreedit = FcitxInputStateGetClientPreedit(input);
    FcitxCandidateWordSetPageSize(FcitxInputStateGetCandidateList(input),
                                  CAND_WORD_NUM);
    FcitxMessagesSetMessageCount(msgPreedit, 0);
    FcitxMessagesSetMessageCount(msgClientPreedit, 0);
    // clean up window asap
    FcitxInstanceCleanInputWindow(en->owner);
    ConfigEn(en);

    // use algorithm to get candidate word
    vector<Word> result = en->engine->do_query(string(en->buf), en->num_flag);
    for (auto &i : result) {
        FcitxCandidateWord cw;
        cw.callback = FcitxEnGetCandWord;
        cw.owner = en;
        cw.priv = NULL;
        cw.strExtra = strdup(
            key_transform(i.latin, en->engine->imConfig->get_key_switch()).c_str());
        cw.strWord = strdup(i.word.c_str());
        cw.wordType = MSG_OTHER;
        FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &cw);
    }
    en->num_flag = 0;

    // setup cursor
    FcitxInputStateSetShowCursor(input, true);
    FcitxInputStateSetCursorPos(input, en->cur);
    FcitxInputStateSetClientCursorPos(input, en->cur);
    FcitxMessagesAddMessageAtLast(msgPreedit, MSG_INPUT, "%s", en->buf);
    FcitxMessagesAddMessageAtLast(msgClientPreedit, MSG_OTHER, "%s%c", en->buf,
                                  ' ');
    return IRV_DISPLAY_CANDWORDS;
}

INPUT_RETURN_VALUE GetRemindCandWords(void *arg) {
    FcitxEn *en = (FcitxEn *)arg;
    FcitxInputState *input = FcitxInstanceGetInputState(en->owner);
    if (!en->strRemindSource[0])
        return IRV_TO_PROCESS;
    FcitxMessages *msgPreedit = FcitxInputStateGetPreedit(input);
    FcitxMessages *msgClientPreedit = FcitxInputStateGetClientPreedit(input);
    FcitxMessagesSetMessageCount(msgPreedit, 0);
    FcitxMessagesSetMessageCount(msgClientPreedit, 0);
    FcitxMessages *msg_up = FcitxInputStateGetAuxUp(input);
    FcitxMessagesSetMessageCount(msg_up, 0);

    // 显示蒙古文联想词
    FcitxMessagesAddMessageStringsAtLast(msg_up, MSG_INPUT,
                                         en->strRemindSource);
    FcitxMessagesAddMessageStringsAtLast(msg_up, MSG_TIPS, _(":"));
    vector<Word> result = en->engine->query_next(string(en->strRemindSource));
    if (result.empty())
        return IRV_TO_PROCESS;
    for (auto &i : result) {
        FcitxCandidateWord cw;
        cw.callback = FcitxEnGetCandWord;
        cw.owner = en;
        cw.priv = NULL;
        cw.strExtra = strdup(
            key_transform(i.latin, en->engine->imConfig->get_key_switch()).c_str());
        cw.strWord = strdup(i.word.c_str());
        cw.wordType = MSG_OTHER;
        FcitxCandidateWordAppend(FcitxInputStateGetCandidateList(input), &cw);
    }
    FcitxInputStateSetIsInRemind(
        input, (FcitxCandidateWordPageCount(
                    FcitxInputStateGetCandidateList(input)) != 0));
    return IRV_DISPLAY_CANDWORDS;
}

void InputSymbols(const string &symbols, FcitxEn *en) {
    FcitxIM *im = FcitxInstanceGetCurrentIM(en->owner);
    if (NULL == strcmp(im->uniqueName, "Oyun")) {
        cout << symbols << "   oyun" << endl;
        // 激活当前输入的光标
        system("cat ~/.window_id | xargs xdotool windowactivate --sync");
        system("fcitx-remote -s Oyun");
        // 把符号写进缓冲区里
        strcpy(en->engine->imConfig->asr_content, symbols.c_str());
        system("fcitx-remote -s Oyun");
        // 触发doinput函数
        system("xdotool key U3F");
        system("fcitx-remote -s Oyun");
        // 保存当前的光标位置
        system("xdotool getactivewindow > ~/.window_id");
        system("fcitx-remote -s Oyun");
    }
}

INPUT_RETURN_VALUE FcitxEnGetCandWord(void *arg, FcitxCandidateWord *candWord) {
    FcitxEn *en = (FcitxEn *)candWord->owner;
    FcitxInputState *input = FcitxInstanceGetInputState(en->owner);
    en->prev = candWord->strExtra;
    // 格后缀前面不加空格
    if (en->prev.substr(0, 1) == "-" || en->prev.substr(0, 1) == "_") {
        sprintf(FcitxInputStateGetOutputString(input), "%s", candWord->strWord);
    } else {
        sprintf(FcitxInputStateGetOutputString(input), " %s",
                candWord->strWord);
    }
    if (FcitxInputStateGetIsInRemind(input)) {
        //        char *pBase = NULL;
        strcpy(en->strRemindSource, en->prev.c_str());
        //        strcpy(FcitxInputStateGetOutputString(input),
        //        en->strRemindSource); pBase = en->strRemindSource;s
        //        strcpy(FcitxInputStateGetOutputString(input), pBase);
        FcitxCandidateWordReset(FcitxInputStateGetCandidateList(input));
        INPUT_RETURN_VALUE retVal = GetRemindCandWords(en);
        if (retVal == IRV_DISPLAY_CANDWORDS)
            return IRV_COMMIT_STRING_REMIND;
        else
            return IRV_COMMIT_STRING;
    }
    return IRV_COMMIT_STRING;
}

/**
 * @brief Destroy the input method while unload it.
 *
 * @return int
 **/
__EXPORT_API void FcitxEnDestroy(void *arg) {
    FcitxEn *en = (FcitxEn *)arg;
    free(en->buf);
    free(arg);
}

static void FcitxEnReloadConfig(void *arg) {
    FcitxEn *en = (FcitxEn *)arg;
    LoadEnConfig(&en->config);
    ConfigEn(en);
}

boolean LoadEnConfig(FcitxEnConfig *fs) {
    // none at the moment
    return true;
}

void SaveEnConfig(FcitxEnConfig *fc) {
    // none at the moment
}

void ConfigEn(FcitxEn *en) {
    // none at the moment
}
